/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2010 huliqing, huliqing.cn@gmail.com
 *
 * This file is part of QFaces.
 * QFaces is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * QFaces is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with QFaces.  If not, see <http://www.gnu.org/licenses/>.
 *
 * 这个文件是QFaces的一部分。
 * 您可以单独使用或分发这个文件，但请不要移除这个头部声明信息.
 * QFaces是一个自由软件，您可以自由分发、修改其中的源代码或者重新发布它，
 * 新的任何修改后的重新发布版必须同样在遵守LGPL3或更后续的版本协议下发布.
 * 关于LGPL协议的细则请参考COPYING、COPYING.LESSER文件，
 * 您可以在QFaces的相关目录中获得LGPL协议的副本，
 * 如果没有找到，请连接到 http://www.gnu.org/licenses/ 查看并获取副本。
 *
 * - Author: Huliqing
 * - Contact: huliqing.cn@gmail.com
 * - License: GNU Lesser General Public License (LGPL)
 * - Blog and source code availability: http://www.huliqing.name/
 */

Q.F.UIValidator = {
	init : function(componentClass, ID_CLIENT, ID_FLAG, ID_MESSAGE, state, 
			_for, message, messageError, messageCorrect, minLength, maxLength, min, max, required, type, equalId, regex, 
			listenerExp, event, interval, onError, onCorrect, displayFlag, imgSrcError, imgSrcCorrect, 
			styleClass, styleClassError, styleClassCorrect, styleClassTargetError, styleClassTargetCorrect) {
	
		var validator = new Q.F.UIValidator.Validator(componentClass, ID_CLIENT, ID_FLAG, ID_MESSAGE, state,
				_for, message, messageError, messageCorrect, minLength, maxLength, min, max, required, type, equalId, regex, 
				listenerExp, event, interval, onError, onCorrect, displayFlag, imgSrcError, imgSrcCorrect, 
				styleClass, styleClassError, styleClassCorrect, styleClassTargetError, styleClassTargetCorrect);
		Q.F.put(ID_CLIENT, validator);
	}
}
Q.F.UIValidator.Validator = function(componentClass, ID_CLIENT, ID_FLAG, ID_MESSAGE, state,
		_for, message, messageError, messageCorrect, minLength, maxLength, min, max, required, type, equalId, regex, 
		listenerExp, event, interval, onError, onCorrect, displayFlag, imgSrcError, imgSrcCorrect, 
		styleClass, styleClassError, styleClassCorrect, styleClassTargetError, styleClassTargetCorrect) {
	this.componentClass = componentClass;
	this.ID_CLIENT = ID_CLIENT;
	// The "flagObj" include "flagImg"
	this.flagObj = Q.get(ID_FLAG);
	this.flagImg = null;
	this.messageObj = Q.get(ID_MESSAGE);
	// -1 -> error, 0 -> unknow, 1 -> correct
	this.state = parseInt(state, 10);
	this._for = _for;
	this.message = message;
	this.messageError = messageError;
	this.messageCorrect = messageCorrect;
	this.minLength = (minLength != "" ? parseInt(minLength, 10) : null);
	this.maxLength = (maxLength != "" ? parseInt(maxLength, 10) : null);
	this.min = (min != "" ? parseInt(min, 10) : null);
	this.max = (max != "" ? parseInt(max, 10) : null);
	this.required = (required == "true");
	// String,Number,Integer
	this.type = type; 
	this.equalId = equalId;
	this.regex = regex;
	this.listenerExp = listenerExp;
	// onblur,onkeyup,interval
	this.event = event;
	this.interval = parseInt(interval, 10);
	this.onError = new Function(onError);
	this.onCorrect = new Function(onCorrect);
	this.displayFlag = (displayFlag == "true");
	this.imgSrcError = imgSrcError;
	this.imgSrcCorrect = imgSrcCorrect;
	
	this.styleClass = (styleClass != "" ? styleClass : null);
	this.styleClassError = (styleClassError != "" ? styleClassError : null);
	this.styleClassCorrect = (styleClassCorrect != "" ? styleClassCorrect : null);
	this.styleClassTargetError = (styleClassTargetError != "" ? styleClassTargetError : null);
	this.styleClassTargetCorrect = (styleClassTargetCorrect != "" ? styleClassTargetCorrect : null);
	
	this.target = null;		// 目标组件
	this.initTryCount = 0;	// 这个属性主要是限制init的偿试次数，因为如果_for所指定的目标组件不存在，那么init偿试一定次数之后应该放弃
	this.focus = false;		// 表示目标(target)当前是否获得焦点
	this.lastValidateValue; // 这里保存了target的最后一次被validate的值。
	this.lastEventInvoke;	// 最后一次调用的event函数,可取值 "onCorrect","onError",这个属性用于避免多次重复调用onError,onCorrect函数
	this.errorCode = "";	// 该参数描述组件验证过程中的错误,错误类型:required,minLength,maxLength,equalId,regex,type,min,max,listener
		
	// ---- Interface
	
	/**
	 * 返回当前的验证状态，-1表示验证不能过，0表示未验证或状态未知，1表示验证成功
	 */
	this.getState = function() {
		return this.state;
	}
	
	/**
	 * 该方法返回组件验证过程中的错误代码，当发生验证错误时，可通过调用这个方法来获得错误代码。返回的错误代码与组件的参数相对应.
	 * 如，发生required验证错误时方法返回"required"; 发生minLength验证不能通过时返回"minLength"; 发生equalId验证不能通过时返回"equalId"...
	 */
	this.getErrorCode = function() {
		return this.errorCode;
	}
	
	// ---- Init
	
	this.init = function(validator) {
		Q.debug("find component,for id=" + validator._for);
		validator.initTryCount++;
		if (validator.initTryCount > 1000) return; // 放弃,超时
		validator.target = Q.get(validator._for);
	    if (validator.target == null) {
	    	Q.U.setTimeout(validator.init, 50, validator);
	    } else {
	    	Q.debug("找到目标组件,initTryCount=" + validator.initTryCount);
	    	// 载入初始状态
	    	validator.initByState();
	    	if (validator.event == "onblur") {
	    		Q.debug("set event=onblur");
	    		Q.U.attachEvent(validator.target, "onblur", function() {return validator.validate();});
	    	} else if (validator.event == "interval" || validator.event == "onkeyup") {
	    		Q.debug("set event=interval");
	    		Q.U.attachEvent(validator.target, "onblur", function() {return validator.onblur();});
	    		Q.U.attachEvent(validator.target, "onfocus", function() {return validator.onfocus();});
	    	}
	    }
	}
	
	this.initByState = function() {
		Q.debug("Load init state...");
		this.flagImg = document.createElement("img");
		this.flagObj.appendChild(this.flagImg);
		this.messageObj.innerHTML = this.message;
		// 必须保存这个状态，以供复原原样式
		this.target.classNameOld = this.target.className;
		this.updateAllState(this.state);
	}
	
	// About "interval" validate
	
	this.onblur = function() {
		this.focus = false;
	}
	
	this.onfocus = function() {
		this.focus = true;
		if (this.event == "interval")
			this.loop(this);
	}
	
	this.loop = function(_self) {
		Q.debug("loop...");
		if (!_self.focus) {
			Q.debug("Stop loop");
			return;
		}
		_self.validate();
		Q.U.setTimeout(_self.loop, _self.interval, _self);
	}
	
	// ---- flag and message
	
	// callback标记是否需要调用后续函数
	this.updateAllState = function(state, callback) {
		this.state = state;
		// 复原状态,如果目标验证正确，并且为空,则去除验证状态
		if (this.state == 1 && this.target.value == "") {
			this.state = 0;
		}
		this.updateFlag(this.state);
		this.updateMessage(this.state);
		this.updateTarget(this.state);
		
		// 清空错误状态。
		if (this.state != -1) {this.errorCode = "";}
		// .在用户将input中内容清空之后，应该清空lastEventInvoke,这样在重新输入内容时会重新响应callback
		// .在用户将input中内容清空之后，应该清空lastValidateValue,这样在重新输入内容时会重新响应
		if (this.state == 0) {
			this.lastEventInvoke = "";
			this.lastValidateValue = "";
		} 
		if (callback) {
			if (this.state == 1 && this.lastEventInvoke != "onCorrect") {
				this.lastEventInvoke = "onCorrect";
				this.onCorrect.apply();
			} else if (this.state == -1 && this.lastEventInvoke != "onError") {
				this.lastEventInvoke = "onError";
				this.onError.apply();
			}
		}
	}
	
	this.updateFlag = function(state) {
		Q.debug("displayFlag");
		if (!this.displayFlag) {
			Q.U.close(this.flagObj);
			return;
		}	
		if (state == 0) {
			Q.U.close(this.flagObj);
		} else {
			Q.U.open(this.flagObj);
			this.flagImg.src = (state == 1 ? this.imgSrcCorrect : this.imgSrcError);		
		}
	}
	this.updateMessage = function(state) {
		Q.debug("displayMessage");
		if (state == 0) {
			this.messageObj.innerHTML = this.message;
			this.messageObj.className = this.styleClass;
		} else if (state == -1) {
			this.messageObj.innerHTML = this.messageError;
			this.messageObj.className = this.styleClassError;
		} else if (state == 1) {
			this.messageObj.innerHTML = this.messageCorrect;
			this.messageObj.className = this.styleClassCorrect;
		}
	}
	this.updateTarget = function(state) {
		Q.debug("displayTarget");
		this.target.className = this.target.classNameOld;
		if (state == -1 && this.styleClassTargetError) {
			this.target.className = this.styleClassTargetError;
		} else if (state == 1 && this.styleClassTargetCorrect) {
			this.target.className = this.styleClassTargetCorrect;
		}
	}
	
	// ---- validate
	
	this.validate = function() {
		Q.debug("== validate ==");
		
		if (this.isValidate()) {
			Q.debug("开始验证");
			// 保存最后一次经过验证的值
			this.lastValidateValue = this.target.value;
			var valid = (this.validateRequired() && this.validateLength() && this.validateEqual() && this.validateRegex() && this.validateType() && this.validateVolume());
			Q.debug("------valid result=" + valid);
			// A.如果前面几个都验证通过，那么还需要通过listener的ajax验证.
			if (valid) {
				this.state = 1;
				this.validateListener();
			}
			// B.如果前面有部分验证不通过，那么也不需要再通过listener来验证了（浪费资源，反正已经不通过了。）,直接更新状态就OK。
			else {
				this.state = -1;
				this.updateAllState(this.state, true);
			}
		} else {
			Q.debug("不需要验证!");
		}
	}
	
	// 判断是否应该进行验证
	this.isValidate = function() {
		if (this.target.value != this.lastValidateValue)
			return true;
		if (this.required && this.target.value == "")
			return true;
		if (this.equalId != "")
			return true;
		return false;
	}

	// v "required"
	this.validateRequired = function() {
		var result = true;
		if (this.required) {
			result = (this.target.value != "");
		}
		if (!result) this.errorCode = "required";
		Q.debug(",,,,,,1.validateRequired, required=" + this.required + ", result=" + result);
		return result;
	}
	
	// v "minLength" and "maxLength"
	this.validateLength = function() {
		var result = true;
		if (this.target.value != "") {
			if (this.minLength != null && this.target.value.length < this.minLength) {
				result = false;
				this.errorCode = "minLength";
			}
			if (this.maxLength != null && this.target.value.length > this.maxLength) {
				result = false;
				this.errorCode = "maxLength";
			}
		}
		Q.debug(",,,,,,2.validateLength, minLength=" + this.minLength
				+ ", maxLength=" + this.maxLength 
				+ ", targetLength=" + this.target.value.length
				+ ", result=" + result);
		return result;
	}
	
	// v "equalId"
	this.validateEqual = function() {
		var result = true;
		if (this.equalId != "" && this.equalId != "null") {
			var equalObj = Q.get(this.equalId);
			result = (equalObj != null && equalObj.value == this.target.value);
		}
		if (!result) this.errorCode = "equal";
		Q.debug(",,,,,,3.validateEqual,equalId=" + this.equalId + ", result=" + result);
		return result;
	}
	
	// v "regex"
	this.validateRegex = function() {
		var result = true;
		if (this.target.value != "" && this.regex != "") {
			var pattern = new RegExp(this.regex);
			result = (this.target.value.match(pattern) != null);
		}
		if (!result) this.errorCode = "regex";
		Q.debug(",,,,,,4.validateRegex, regex=" + this.regex + ", result=" + result);
		return result;
	}
	
	// v "type", String/Number/Integer
	this.validateType = function() {
		var result = true;
		if (this.target.value != "" && this.type != "") {
			if (this.type.toLowerCase() == "number")
				result = this.isNumber(this.target.value);
			if (this.type.toLowerCase() == "integer")
				result = this.isInteger(this.target.value);
		}
		if (!result) this.errorCode = "type";
		Q.debug(",,,,,,5.validateType, type=" + this.type + ", result=" + result);
		return result;
	}
	
	// v "min" and "max"
	this.validateVolume = function() {
		var result = true;
		if (this.target.value != "" && this.isNumber(this.target.value)) {
			var temp = Number(this.target.value);
			if (this.min != null && temp < this.min) {
				result = false;
				this.errorCode = "min";
			}
			if (this.max != null && temp > this.max) {
				result = false;
				this.errorCode = "max";
			}
		}
		Q.debug(",,,,,,6.validateVolume, min=" + this.min + ", max=" + this.max + ", result=" + result);
		return result;
	}

	// v "listener"
	this.validateListener = function() {
		var _self = this;
		Q.debug("validateListener,listenerExp=" + _self.listenerExp);
		// 无绑定或无值的情况下不验证
		if (_self.listenerExp == null || _self.listenerExp == "" || _self.target.value == "") {
			// 如果前面几步验证都通过，那么会执行这一步，如果这一步不存在(没有绑定listener)
			// 那么应该立即更新状态。因为前面几步的验证结果需要与这一步结合。
			_self.updateAllState(_self.state, true);
			return;
		}
		Q.debug("Ajax validate start...");
		_self.flagObj.appendChild(Q.F.getLoading());
        Q.F.loading(true);
        var ajax = Q.F.Ajax.build(_self.ID_CLIENT);
        ajax.put("componentClass", _self.componentClass);
        ajax.put("listenerExp", _self.listenerExp);
        ajax.put("targetValue", _self.target.value);
        var processWhenOK = function() {
        	Q.F.loading(false);
        	var valid = (ajax.request.responseText == "true");
        	Q.debug("listener validate=" + valid);
        	if (!valid) _self.errorCode = "listener";
        	_self.state = (valid && _self.state == 1) ? 1 : -1;
        	_self.updateAllState(_self.state, true);
        }
        ajax.setProcess(processWhenOK);
        ajax.get();
	}
	
	// 判断是否为Number类型
	this.isNumber = function(value) {
		return (/^-?\d*\.?\d+$/.test(value));
	}
	// 判断是否为Integer类型
	this.isInteger = function(value) {
		return (/^-?\d+$/.test(value));
	}
	// 初始化
	this.init(this);
}









